package Engine;

import Engine.PhysicEngine.*;
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.newdawn.slick.opengl.Texture;
import static org.lwjgl.opengl.GL11.*;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.util.ArrayList;

public class Engine {

	public int WINDOW_WIDTH = 800;
	public int WINDOW_HEIGHT = 600;
	public int FULLSCREEN_WIDTH;
	public int FULLSCREEN_HEIGHT;
	long lastFrame;
	long lastFPS;
	int fps;
	public String Version = "(Alpha) 0.07 | MapGenerator";
	public Physic _physic;
	private java.util.List<RenderObject> objects;
	public Time time;
	public TextRenderer textRenderer;
	public float zoom = 1;
	private float xPos;
	private float yPos;
	private MemoryMXBean mf;

	private boolean isInit = false;

	public void AddRenderComponent(RenderObject obj) {
		objects.add(obj);
		if (isInit) {
			for (RenderObject item : objects) {
				if (item instanceof PhysicObject)
					((PhysicObject) item).init(_physic);
				else
					item.RenderObject_init();
			}
		}
	}

	public int getHeight() {
		return Display.getHeight();
	}

	public int getWidth() {
		return Display.getWidth();
	}

	public Engine(String title) {
		mf = ManagementFactory.getMemoryMXBean();
		objects = new ArrayList<RenderObject>();

	}

	private void InitGL() {

		time = new Time();
		_physic = new Physic(this);
		Display.setVSyncEnabled(true);
		glEnable(GL_TEXTURE_2D);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
				GL_LINEAR_MIPMAP_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
				GL_LINEAR_MIPMAP_LINEAR);
		// glEnable(GL_DEPTH_TEST);
		glEnable(GL_ALPHA_TEST);
		glClearColor(145.0f / 255.0f, 177.0f / 255.0f, 237.0f / 255.0f, 1f);

		// enable alpha blending
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

		if (Display.isFullscreen()) {
			glViewport(0, 0, FULLSCREEN_WIDTH, FULLSCREEN_HEIGHT);
		} else {
			glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);
		}

		glMatrixMode(GL_MODELVIEW);

		glMatrixMode(GL_PROJECTION);

		glLoadIdentity();

		if (Display.isFullscreen()) {
			glOrtho(0, FULLSCREEN_WIDTH, FULLSCREEN_HEIGHT, 0, 100, -100);
		} else {
			glOrtho(0, WINDOW_WIDTH, WINDOW_HEIGHT, 0, 100, -100);
		}

		glMatrixMode(GL_MODELVIEW);

		textRenderer = new TextRenderer(this);

		UpdateCamera(0, 0);

		for (RenderObject item : objects) {
			if (item instanceof PhysicObject)
				((PhysicObject) item).init(_physic);
			else
				item.RenderObject_init();
		}
		isInit = true;

	}

	public void UpdateCamera(float x, float y) {
		setCameraxPos(x);
		setCamerayPos(y);
		// zoom = 2;
		textRenderer.line4 = "Camera: X: " + getCameraxPos() + " Y: "
				+ getCamerayPos() + " ZOOM: " + zoom;
		glLoadIdentity();
		glTranslated(0.5 * Display.getWidth(), 0.5 * Display.getHeight(), 0);
		glScaled(zoom, zoom, 1);

		glTranslated(-0.5 * Display.getWidth(), -0.5 * Display.getHeight(), 0);
		glTranslatef(-x, -y, 0);

	}

	public void UpdateCamera(float x, float y, float zoom) {

		glLoadIdentity();
		glTranslated(0.5 * Display.getWidth(), 0.5 * Display.getHeight(), 0);
		glScaled(zoom, zoom, 1);
		glTranslated(-0.5 * Display.getWidth(), -0.5 * Display.getHeight(), 0);

		glTranslatef(-x, -y, 0);

	}

	public void setDisplayMode(int width, int height, boolean fullscreen) {
		zoom = (float) height / 250f;
		// return if requested DisplayMode is already set
		if ((Display.getDisplayMode().getWidth() == width)
				&& (Display.getDisplayMode().getHeight() == height)
				&& (Display.isFullscreen() == fullscreen)) {
			return;
		}

		try {
			DisplayMode targetDisplayMode = null;

			if (fullscreen) {
				DisplayMode[] modes = Display.getAvailableDisplayModes();
				int freq = 0;

				for (int i = 0; i < modes.length; i++) {
					DisplayMode current = modes[i];

					if ((current.getWidth() == width)
							&& (current.getHeight() == height)) {
						if ((targetDisplayMode == null)
								|| (current.getFrequency() >= freq)) {
							if ((targetDisplayMode == null)
									|| (current.getBitsPerPixel() > targetDisplayMode
											.getBitsPerPixel())) {
								targetDisplayMode = current;
								freq = targetDisplayMode.getFrequency();
							}
						}

						// if we've found a match for bpp and frequence against
						// the
						// original display mode then it's probably best to go
						// for this one
						// since it's most likely compatible with the monitor
						if ((current.getBitsPerPixel() == Display
								.getDesktopDisplayMode().getBitsPerPixel())
								&& (current.getFrequency() == Display
										.getDesktopDisplayMode().getFrequency())) {
							targetDisplayMode = current;
							break;
						}
					}
				}
			} else {
				targetDisplayMode = new DisplayMode(width, height);
			}

			if (targetDisplayMode == null) {
				System.out.println("Failed to find value mode: " + width + "x"
						+ height + " fs=" + fullscreen);
				return;
			}

			Display.setDisplayMode(targetDisplayMode);
			Display.setFullscreen(fullscreen);

		} catch (LWJGLException e) {
			System.out.println("Unable to setup mode " + width + "x" + height
					+ " fullscreen=" + fullscreen + e);
		}
	}

	public void LoadResources() {

	}

	private void Render() {

		textRenderer.line2 = "FPS: " + time.getFps() + " Delta: "
				+ time.getDelta() + " Ram: " + mf.getHeapMemoryUsage();

		for (RenderObject item : objects) {
			if (item instanceof PhysicObject)
				((PhysicObject) item).update(time);

			item.RenderObject_draw();
			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		}

		for (RenderObject item : objects) {
			item.RenderObject_gui();
			glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		}
		UpdateCamera(0, 0, 1);
		textRenderer.Render();
		UpdateCamera(getCameraxPos(), getCamerayPos());
	}

	public void start() {

		try {
			FULLSCREEN_HEIGHT = Display.getDisplayMode().getHeight();
			FULLSCREEN_WIDTH = Display.getDisplayMode().getWidth();
			setDisplayMode(WINDOW_WIDTH, WINDOW_HEIGHT, false);
			Display.create();
		} catch (LWJGLException e) {
			e.printStackTrace();
			System.exit(0);
		}

		InitGL();
		LoadResources();
		// init OpenGL here

		while (!Display.isCloseRequested()) {

			time.updateFPS();

			while (Keyboard.next()) {
				if (Keyboard.getEventKeyState()) {
					if (Keyboard.getEventKey() == Keyboard.KEY_F3) {
						textRenderer.DrawDebug = !textRenderer.DrawDebug;
					}
					if (Keyboard.getEventKey() == Keyboard.KEY_F11) {
						if (Display.isFullscreen()) {
							setDisplayMode(WINDOW_WIDTH, WINDOW_HEIGHT, false);
						} else {
							setDisplayMode(FULLSCREEN_WIDTH, FULLSCREEN_HEIGHT,
									true);
						}

						InitGL();
					}

				}
			}

			// Clear the screen and depth buffer
			glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

			Render();
			// render OpenGL here

			Display.update();
			// Display.sync(600);
		}

		Display.destroy();
	}

	public float getCameraxPos() {
		return xPos;
	}

	public void setCameraxPos(float xPos) {
		this.xPos = xPos;
	}

	public float getCamerayPos() {
		return yPos;
	}

	public void setCamerayPos(float yPos) {
		this.yPos = yPos;
	}

}
